<?php

namespace app\chat;

use app\common\cachemodel\live\LiveAnchorInfo;
use app\common\cachemodel\live\LiveUserContribution;
use app\common\cachemodel\Recode;
use app\common\cachemodel\zlcr3\Zlcr3ConsultAccount;
use app\common\cachemodel\ZlcrAstr;
use app\common\cachemodel\ZlUserVip;
use app\common\lib\Aes;
use app\common\lib\exception\ApiException;
use app\common\lib\IAuth;
use app\common\model\User;
use app\zyhsapi\model\ZyhsUser;
use GatewayWorker\Lib\Gateway;
use think\Cache;
use think\Log;
use XznsTools\cache\CacheManage;
use XznsTools\cache\zyhs\CacheManageZyhs;

/**
 * 直播间 onMessage 的处理
 */
class LiveMessage
{
    //日志前缀
    public const LOG_PRE = '直播聊天MyEvent：';

    /**
     * 消息的类型：登录
     */
    public const MSG_TYPE_LOGIN = 'login';
    //消息的类型
    /**
     * 消息的类型：在线人数和热度信息
     */
    public const MSG_TYPE_ONLINE_INFO = 'online_info';
    /**
     * 消息的类型：顶部“荣升榜一”返回的数据
     */
    public const MSG_TYPE_POP_HEADER = 'pop_header';
    /**
     * 消息的类型：发送礼物的消息
     */
    public const MSG_TYPE_POP_GIFT = 'pop_gift';
    /**
     * 消息的类型：**进入直播间
     */
    public const MSG_TYPE_POP_USER_COME = 'pop_user_come';
    /**
     * 消息的类型：点赞的星座（范围1~12）【自己的点赞，App端自己加，这里返回的都是别人的点赞】
     */
    public const MSG_TYPE_POP_ZAN_SIGN = 'pop_zan_sign';
    /**
     * 消息的类型：主播推荐商品
     */
    public const MSG_TYPE_POP_RECOMMEND = 'pop_recommend';
    /**
     * 消息的类型：主播发放优惠券
     */
    public const MSG_TYPE_POP_COUPON = 'pop_coupon';
    /**
     * 消息的类型：开始抽奖
     */
    public const MSG_TYPE_POP_AWARD_START = 'pop_award_start';
    /**
     * 消息的类型：抽奖结果
     */
    public const MSG_TYPE_POP_AWARD_END = 'pop_award_end';
    /**
     * 消息的类型：发送的消息
     */
    public const MSG_TYPE_POP_MSG = 'pop_msg';
    /**
     * 消息的类型：开启/关闭 摄像头/麦克风
     */
    public const MSG_TYPE_CHANGE_CAMERA_RADIO = 'change_camera_radio';
    /**
     * 消息的类型：1：禁言；（App端不发送数据给服务器端）；2：解封禁言；（App端发送数据给服务器端）；3：踢出直播间；（App端，断开直播，断开websoket）
     */
    public const MSG_TYPE_ACTION_USER = 'action_user';
    /**
     * 消息的类型：关闭推荐商品
     */
    public const MSG_TYPE_CLOSE_RECOMMEND_GOODS = 'close_recommend_goods';
    /**
     * 消息的类型：直播结束
     */
    public const MSG_TYPE_LIVE_STATUS = 'live_status';
    /**
     * 消息的类型：发送给主播的订单消息
     */
    public const MSG_TYPE_POP_MSG_LIVER_ORDER = 'pop_msg_liver_order';

    //2.7.4 连麦功能新增的
    /**
     * 消息的类型：主播状态【0=》主播离开一会（前端展示，主播离开一会的提示[在主播端进入后台之前，通过接口发送消息]）；1=》主播回来了（重新播放视频[在主播端 开始/继续 直播请求接口的时候，发送消息]）；】（由于切换了rtc的sdk，不能把“主播离开一会的图片”推流到前端）
     */
    public const MSG_TYPE_LIVER_STATUS = 'liver_status';
    /**
     * 消息的类型：用户端/主播端/用户主播端，展示的连麦弹出框（注意：1.网络状态，是通过ws发送的消息；2.用户端的消息，重新进入直播间要展示；）
     */
    public const MSG_TYPE_POP_MSG_RTC = 'pop_msg_rtc';
    /**
     * 消息的类型：用户端/主播端/用户主播端，展示的连麦弹出框（注意：1.网络状态，是通过ws发送的消息；2.用户端的消息，重新进入直播间要展示；）
     */
    public const MSG_TYPE_RTC_STATUS = 'rtc_status';
    /**
     * 消息类型：显示toast提示
     */
    public const MSG_TYPE_TOAST = 'toast';
    //2.7.4 连麦功能新增的
    // 2.7.5 解答新增
    /**
     * 看盘、骰子、塔罗 浮标消息
     */
    public const MSG_TYPE_POP_LIVE_ANSWER_ICON = 'pop_live_answer_icon';
    /**
     * 解答功能。问题状态刷新。
     */
    public const MSG_TYPE_POP_ANSWER_REFRESH = 'pop_answer_refresh';
    /**
     * 用户解答响应状态
     */
    public const MSG_TYPE_POP_LIVE_ANSWER_RESPONSE_STATUS = 'pop_live_answer_response_status';
    /**
     * 解答 开始
     */
    public const MSG_TYPE_POP_LIVE_ANSWER_OPEN = 'pop_live_answer_open';

    /**
     * 连麦咨询 咨询问题状态刷新
     */
    public const MSG_TYPE_POP_CONSULT_DETAIL_REFRESH = 'pop_consult_detail_refresh';

    /**
     * 连麦咨询 发送咨询问题
     */
    public const MSG_TYPE_POP_CONSULT_MSG = 'pop_consult_msg';

    /**
     * 连麦咨询 发送 准有好事用户端 连线结束 打点数据
     */
    public const MSG_TYPE_POP_CONSULT_CLOSE_EVENT_MARKING = 'pop_consult_close_event_marking';
    /**
     * 连麦咨询 进入RTC频道
     */
    public const MSG_TYPE_POP_CONSULT_RTC_JOIN = 'pop_consult_rtc_join';

    /**
     * 连麦咨询 语音咨询 待接听
     */
    public const MSG_TYPE_POP_VOICE_CONSULT_CALL = 'pop_voice_consult_call';

    /**
     * 关闭咨询连线舌头，并弹toast
     */
    public const MSG_TYPE_POP_CONSULT_TOAST = 'pop_consult_toast';

    /**
     * 优质连线 直播间 发送消息
     */
    public const MSG_TYPE_POP_GOOD_RTC_LIVE_MSG = 'pop_good_rtc_live_msg';

    /**
     * 优质连线 直播间 在线人数消息
     */
    public const MSG_TYPE_GOOD_RTC_LIVE_ONLINE_MSG = 'good_rtc_live_online_msg';

    /**
     * 优质连线 直播间 关播
     */
    public const MSG_TYPE_GOOD_RTC_LIVE_CLOSE = 'good_rtc_live_close';

    /**
     * 优质连线 直播间 进入直播间消息
     */
    public const MSG_TYPE_POP_GOOD_RTC_LIVE_USER_JOIN = 'pop_good_rtc_live_user_join';

    // 2.7.5 解答新增

    //发送给谁的类型【
    //  WHO_TYPE_ALL（all）=》发送给所有人（群组名字为：$live_id）；
    //  WHO_TYPE_UID（uid）：发送给某个人；
    //  WHO_TYPE_LIVER（liver）：发送给主播端 和 后台管理的ws用户 和 用户主播管理员（群组名字为：$live_id_liver_or_admin）
    //  WHO_TYPE_CLIENT_ID（client_id）：发送给某个人；
    //  WHO_TYPE_APP_USER（app_user_group）：只发送给准了app的用户（群组名字为：$live_id_app_user_group）；
    //】
    /**
     * 发送给谁的类型：发送给所有人（群组名字为：$live_id）
     */
    public const WHO_TYPE_ALL = 'all';
    /**
     * 发送给谁的类型：发送给某个uid
     */
    public const WHO_TYPE_UID = 'uid';
    /**
     * 发送给谁的类型：发送给主播端 和 后台管理的ws用户 和 用户主播管理员群组（群组名字为：$live_id_liver_or_admin）
     */
    public const WHO_TYPE_LIVER = 'liver';
    /**
     * 发送给谁的类型：发送给某个client_id
     */
    public const WHO_TYPE_CLIENT_ID = 'client_id';
    /**
     * 发送给谁的类型：只发送给准了app的用户群组（群组名字为：$live_id_app_user_group）
     */
    public const WHO_TYPE_APP_USER = 'app_user_group';

    /**
     *  发送给谁的类型：发送给某个占星师
     */
    public const WHO_TYPE_ASTRO = 'astro';

    /**
     * 发送给谁的类型：发送给某个 准有好事 uid
     */
    public const WHO_TYPE_ZYHS_UID = 'zyhs_uid';
    /**
     * 发送给的的类型：优质连线直播间房间
     */
    public const WHO_TYPE_GOOD_RTC_LIVE_ROOM = 'good_rtc_live_room';
    //用户类型：【0：用户App端；1：是主播App端；2：是准了后台管理员；3：用户管理员；】
    /**
     * 用户类型：用户App端
     */
    public const USER_TYPE_APP_USER = 0;
    /**
     * 用户类型：是主播App端
     */
    public const USER_TYPE_APP_LIVER = 1;
    /**
     * 用户类型：是准了后台管理员
     */
    public const USER_TYPE_ADMIN = 2;
    /**
     * 用户类型：用户管理员
     */
    public const USER_TYPE_APP_ADMIN = 3;

    /**
     * 用户类型：是占星师App端
     */
    public const USER_TYPE_APP_ASTRO = 4;

    /**
     * 用户类型：准有好事 APP端 用户
     */
    public const USER_TYPE_APP_ZYHS_USER = 5;


//    /**
//     * 处理App端发送来的数据
//     * @param string $action
//     * @param array $params
//     */
//    public static function getAction($action = '', $params = [])
//    {
//
//    }


    /**
     * 获取直播间的在线人数
     * @param string $live_id
     * @return int
     */
    public static function getClientIdCountByGroup(string $live_id = ''): int
    {
        //设置注册地址
        Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');
        return Gateway::getClientCountByGroup($live_id);
    }

    /**
     * 获取直播间的所有在线用户信息
     * @param string $live_id
     * @return array
     */
    public static function getClientSessionsByGroup($live_id = ''): array
    {
        //设置注册地址
        Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');
        return Gateway::getClientSessionsByGroup($live_id);
    }

    /**
     * 获取直播间的所有在线用户 uid
     * @param string $live_id
     * @return array
     */
    public static function getUidListByGroup($live_id = ''): array
    {
        //设置注册地址
        Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');
        return Gateway::getUidListByGroup($live_id);
    }

    /**
     * 获取uid是否在线
     * @param $liveId
     * @param $uid
     * @return bool
     */
    public static function getUidIsOnline($liveId, $uid): bool
    {
        Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');
//        $allSession = Gateway::getAllClientSessions($liveId);
        //获取uid绑定的所有的客户端id
        $clientIds = Gateway::getClientIdByUid($uid);
        foreach ($clientIds as $clientId)
        {
            //获取指定客户端session
            $info = Gateway::getSession($clientId);
            //判断当前直播间是否存在
            if ($liveId === (int)$info['live_id'])
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取与uid绑定的client_id列表（用于给app用户组发送消息的时候，排除某个用户）
     * @param $uid
     * @return array
     */
    public static function getClientIdByUid($uid)
    {
        Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');
        return Gateway::getClientIdByUid($uid);
    }

    /**
     * 发送消息给Client端
     * @param array $who 发送给谁 和 当前用户是谁（当用户发送弹幕的时候，用来排除这个用户）
     * @param string $action 发送的消息类型
     * @param mixed $data 发送的数据
     * @param array $exclude_client_ids 发送组消息的时候，排除哪些clientids
     * @return array 例如：['is_success' => true,'msg' => '',]
     */
    public static function sendMsg(array $who = [], string $action = '', $data = [], array $exclude_client_ids = []): array
    {
        //设置注册地址
        Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');

        //返回数据
        $return = [
            'is_success' => true,
            'msg' => '',
        ];

        $msg = '';//返回的信息

        //开始校验$who参数
        if (!isset($who['type'], $who['id']))
        {
            $return['is_success'] = false;
            $return['msg'] = 'who 参数错误，必须包含 type、id 字段，缺少参数';
        } else
        {
            //开始校验消息数据
            switch ($action)
            {
                case self::MSG_TYPE_ONLINE_INFO:
                {
                    /**
                     * {
                     * "person_num":25444,
                     * "hot_num":"2.5万"
                     * }
                     */
                    //校验参数
                    if (!isset($data['online_num'], $data['hot_num']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，online_info 字段，缺少参数';
                    }
                    $msg = '在线人数和热度信息';
                    break;
                }
                case self::MSG_TYPE_POP_HEADER:
                {
                    /**
                     * {
                     * "continue_second":10,
                     * "title":"恭喜***荣升榜一！",
                     * "target":"",
                     * "link":""
                     * }
                     */
                    //校验参数
                    if (!isset($data['continue_second'], $data['title']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_header 字段，缺少参数';
                    }
                    $msg = '顶部“荣升榜一”返回的数据';
                    break;
                }
                case self::MSG_TYPE_POP_GIFT:
                {
                    /**
                     * {
                     * "user_info":{
                     * "uen":"776d22bdafd9",
                     * "name":"我儿童",
                     * "avatar":"/uploads/avatar/20200925/c6252be325da00742457beaf26c0ae42.png",
                     * "vip_level":4,
                     * "rank_index":-1
                     * },
                     * "gift_info":{
                     * "id":1,
                     * "name":"送出小行星",
                     * "count":10,
                     * "img_url":"/uploads/avatar/20200925/c6252be325da00742457beaf26c0ae42.png",
                     * "coin":500,
                     * "lottie":{
                     * "url":"/mini/divination_or_taro/taro_detail/lottie/data.json",
                     * "width_ratio_of_width":1,
                     * "height_ratio_of_width":1,
                     * "left_ratio_of_width":0,
                     * "bottom_ratio_of_width":0,
                     * "loop_num":2
                     * }
                     * }
                     * }
                     */
                    //校验参数
                    if (!isset($data['user_info']['uen'], $data['user_info']['name'], $data['user_info']['vip_level'], $data['user_info']['rank_index']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_gift->user_info 字段，缺少参数';
                    } else if (!isset($data['gift_info']['id'], $data['gift_info']['name'], $data['gift_info']['count'], $data['gift_info']['img'], $data['gift_info']['coin']))   // 去除了 $data['gift_info']['lottie']
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_gift->gift_info 字段，缺少参数';
                    }
                    $msg = '发送礼物的消息';
                    break;
                }
                case self::MSG_TYPE_POP_USER_COME:
                {
                    /**
                     * {
                     * "user_info":{
                     * "uen":"776d22bdafd9",
                     * "name":"我儿童",
                     * "avatar":"/uploads/avatar/20200925/c6252be325da00742457beaf26c0ae42.png",
                     * "vip_level":4,
                     * "rank_index":-1
                     * },
                     * "des":"我儿童进入直播间"
                     * }
                     */
                    //校验参数
                    if (!isset($data['user_info']['uen'], $data['user_info']['name'], $data['des']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_user_come 字段，缺少参数';
                    }
                    $msg = '**进入直播间';
                    break;
                }
                case self::MSG_TYPE_POP_ZAN_SIGN:
                {
                    /**
                     * "pop_zan_sign":3
                     */
                    //校验参数（注意：当用户未登录的时候，也可以点赞，不过sign为0）
                    if ((int)$data < 0 || (int)$data > 12)
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '点赞星座区间为1~12！';
                    }
                    $msg = '点赞';
                    break;
                }
                case self::MSG_TYPE_POP_RECOMMEND:
                {
                    /**
                     * {
                     * "title":"主播推荐",
                     * "img":"/uploads/avatar/20200925/c6252be325da00742457beaf26c0ae42.png",
                     * "name":"定制报告",
                     * "original_price_des":"原价558元",
                     * "now_price":"558",
                     * "btn_info":{
                     * "name":"现在购买",
                     * "target":"local",
                     * "link":"custom_report_index?need_auth=0"
                     * }
                     * }
                     */
                    //校验参数
                    if (!isset($data['title'], $data['img'], $data['name'], $data['original_price_des'], $data['now_price'], $data['btn_info']['name'], $data['btn_info']['target'], $data['btn_info']['link']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_recommend 字段，必须包含，title、img、name、original_price_des、now_price、btn_info-》name，btn_info-》target，btn_info-》link';
                    }
                    $msg = '主播推荐商品';
                    break;
                }
                case self::MSG_TYPE_POP_COUPON:
                {
                    /**
                     * {
                     * "id":1,
                     * "title":"优惠券",
                     * "img":"/uploads/avatar/20200925/c6252be325da00742457beaf26c0ae42.png",
                     * "name":"学业报告八折券"
                     * }
                     */
                    //校验参数
                    if (!isset($data['id'], $data['title'], $data['img'], $data['name'], $data['is_only_vip_show']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_coupon 字段，必须包含，id、title、img、name、is_only_vip_show';
                    }
                    $msg = '主播发放优惠券';
                    break;
                }
                case self::MSG_TYPE_POP_AWARD_START:
                {
                    /**
                     *{
                     * "id":1,
                     * "title":"主播抽奖",
                     * "continue_second":300,
                     * "list":[
                     * {
                     * "title":"参与方式：",
                     * "des_info":{
                     * "name":"发送指定弹幕",
                     * "is_show_left_time":0,
                     * "target":"",
                     * "link":""
                     * }
                     * },
                     * {
                     * "title":"指定弹幕：",
                     * "des_info":{
                     * "name":"水逆退散！",
                     * "is_show_left_time":0,
                     * "target":"local",
                     * "link":"live_comment?text=%E6%B0%B4%E9%80%86%E9%80%80%E6%95%A3%EF%BC%81&need_auth=2"
                     * }
                     * },
                     * {
                     * "title":"",
                     * "des_info":{
                     * "is_show_left_time":0,
                     * "name":"",
                     * "target":"",
                     * "link":""
                     * }
                     * },
                     * {
                     * "title":"时间：",
                     * "des_info":{
                     * "is_show_left_time":1,
                     * "name":"5分钟",
                     * "target":"",
                     * "link":""
                     * }
                     * },
                     * {
                     * "title":"奖品：",
                     * "des_info":{
                     * "is_show_left_time":0,
                     * "name":"准有好运",
                     * "target":"local",
                     * "link":"live_gift_position?id=1&need_auth=0"
                     * }
                     * },
                     * {
                     * "title":"数量：",
                     * "des_info":{
                     * "is_show_left_time":0,
                     * "name":"3人",
                     * "target":"",
                     * "link":""
                     * }
                     * }
                     * ]
                     * }
                     */
                    //校验参数
                    if (!isset($data['id'], $data['title'], $data['continue_second'], $data['list']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_award_start 字段，必须包含，id、title、continue_second、list字段';
                    }
                    $msg = '开始抽奖';
                    break;
                }
                case self::MSG_TYPE_POP_MSG_LIVER_ORDER:
                case self::MSG_TYPE_POP_MSG:
                {
                    /**
                     * {
                     * "user_info":{
                     * "uen":"776d22bdafd9",
                     * "name":"我儿童",
                     * "avatar":"/uploads/avatar/20200925/c6252be325da00742457beaf26c0ae42.png",
                     * "vip_level":4,
                     * "rank_index":-1
                     * },
                     * "msg_info":{
                     * "color":"#ffffff",
                     * "content":"直播好精彩啊！直播好精彩啊！直播好精彩啊！直播好精彩啊！直播好精彩啊！直播好精彩啊！直播好精彩啊！直播好精彩啊！直播好精彩啊！直播好精彩啊！"
                     * }
                     * }
                     */
                    //校验参数
                    if (!isset($data['msg_info']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_msg 字段，必须包含 msg_info ';
                    } else if (!isset($data['msg_info']['color'], $data['msg_info']['content'], $data['msg_info']['id']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_msg->msg_info 字段，必须包含，id、color、content 字段';
                    } else if (self::MSG_TYPE_POP_MSG_LIVER_ORDER === $action)
                    {//发送给主播的用户下单消息
                        if ($who['type'] !== self::WHO_TYPE_LIVER)
                        {
                            $return['is_success'] = false;
                            $return['msg'] = 'who type必须为 liver';
                        }
                    } else
                    {//发送给所有用户的消息
                        if (!isset($who['uid']))
                        {
                            $return['is_success'] = false;
                            $return['msg'] = '参数错误，$who 必须包含 uid 字段';
                        } else
                        {//获取用户的client_id，放到 $exclude_client_ids 里（用户发送消息，ws不返回给用户）
                            $client_ids_of_uid = Gateway::getClientIdByUid($who['uid']);
                            $exclude_client_ids = array_merge($client_ids_of_uid, $exclude_client_ids);
                        }
                        self::sendmsgCacheAdd($data, $who['id']);
                    }

                    $msg = '发送的消息';
                    break;
                }
                case self::MSG_TYPE_CHANGE_CAMERA_RADIO:
                {
                    /**
                     *{
                     * "camera_status":1,
                     * "radio_status":0
                     * }
                     */
                    //校验参数
                    if (!isset($data['camera_status']) && !isset($data['radio_status']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，change_camera_radio 必须包含 camera_status 或 radio_status';
                    }
                    $msg = '主播的摄像头、麦克风状态变化通知';
                    break;
                }
                case self::MSG_TYPE_ACTION_USER:
                {
                    /**
                     * "action_user":0
                     */
                    //校验参数 直播间用户管理，1：禁言；（App端不发送数据给服务器端）；2：解封禁言；（App端发送数据给服务器端）；3：踢出直播间；（App端，断开直播，断开websoket）
                    if (!in_array((int)$data, [1, 2, 3], true))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，action_user必须在 1~3 之间';
                    }
                    $msg = '直播间用户管理';
                    break;
                }
                case self::MSG_TYPE_CLOSE_RECOMMEND_GOODS:
                {
                    /**
                     * "close_recommend_goods":1
                     */
                    if ((int)$data !== 1)
                    {//1=>关闭推荐商品
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，close_recommend_goods 必须为1';
                    }
                    $msg = '关闭推荐商品';
                    break;
                }
                case self::MSG_TYPE_LIVE_STATUS:
                {
                    /**
                     * "live_status":1
                     */
                    if ((int)$data !== 1)
                    {//1=>直播结束
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，live_status 必须为1';
                    }
                    $msg = '直播结束';
                    break;
                }
                case self::MSG_TYPE_LIVER_STATUS:
                {
                    /**
                     * "liver_status":0
                     */
                    //校验参数（注意：主播状态【0=》主播离开一会（前端展示，主播离开一会的提示[在主播端进入后台之前，通过接口发送消息]）；1=》主播回来了（重新播放视频[在主播端 开始/继续 直播请求接口的时候，发送消息]）；】（由于切换了rtc的sdk，不能把“主播离开一会的图片”推流到前端））
                    if ((int)$data !== 0 && (int)$data !== 1)
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '主播状态为0~1！';
                    }
                    $msg = '主播状态';
                    break;
                }
                case self::MSG_TYPE_RTC_STATUS:
                {
                    /**
                     * "rtc_status":0
                     */
                    if (!in_array((int)$data, [0, 1, 2, 3]))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '连麦状态区间为0~1！';
                    }
                    $msg = '连麦状态';
                    break;
                }

                case self::MSG_TYPE_TOAST:
                {
                    /**
                     * "toast":"123"
                     */
                    if (empty($data))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = 'toast不能为空！';
                    }
                    $msg = '显示toast';
                    break;
                }


                case self::MSG_TYPE_POP_MSG_RTC:
                {
                    //校验参数
                    if (!isset($data['type']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_msg_rtc 字段，必须包含 type ';
                    }

                    $msg = '发送的消息';
                    break;
                }
                case self::MSG_TYPE_POP_LIVE_ANSWER_ICON:
                {
                    //校验参数
                    if (!isset($data['type']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_live_answer_icon 字段，必须包含 type ';
                    }
                    $msg = '发送的消息';
                    break;
                }
                case self::MSG_TYPE_POP_ANSWER_REFRESH:
                {
                    //校验参数
                    if (!isset($data['type']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_answer_refresh 字段，必须包含 type ';
                    }
                    $msg = '发送的消息';
                    break;
                }
                case self::MSG_TYPE_POP_LIVE_ANSWER_RESPONSE_STATUS:
                {
                    //校验参数
                    if (!isset($data['type']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_live_answer_delay 字段，必须包含 type ';
                    }
                    $msg = '发送的消息';
                    break;
                }
                case self::MSG_TYPE_POP_LIVE_ANSWER_OPEN:
                {
                    //校验参数
                    if (!isset($data['type']))
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '参数错误，pop_live_change_pay_type 字段，必须包含 type';
                    }
                    $msg = '发送的消息';
                    break;
                }
                case self::MSG_TYPE_POP_CONSULT_DETAIL_REFRESH:
                case self::MSG_TYPE_POP_CONSULT_MSG:
                case self::MSG_TYPE_POP_CONSULT_CLOSE_EVENT_MARKING:
                    break;
                case self::MSG_TYPE_POP_GOOD_RTC_LIVE_MSG:
                {
                    self::ZyhsGoodRtcLiveSendmsgCacheAdd($data, $who['id']);
                    break;
                }
            }
        }

        if (!$return['is_success'])
        {//发送消息校验失败，记录日志 $who = [], $action = '', $data = [], $exclude_client_ids = []
            Log::write("websocket sendMsg 发送【" . $msg . "】消息校验错误：" . $return['msg'] . '【who=' . json_encode($who, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',action=' . json_encode($action, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',data=' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',exclude_client_ids=' . json_encode($exclude_client_ids, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】', 'error', true);
        } else
        {
            $is_send_now = self::sendLimit($msg, $who, $action, $data, $exclude_client_ids);
            if ($is_send_now)
            {//立刻发送
                self::sendMsgNow($msg, $who, $action, $data, $exclude_client_ids);
            }
        }

        return $return;
    }


    /**
     * 记录最近的消息
     * @param array $msgData
     * @param int $liveId
     */
    private static function sendmsgCacheAdd(array $msgData, int $liveId)
    {
        $len = 20;
        // 获取redis操作对象
        $redis = CacheManage::getInstance()->redisHandler;

        $cacheKey = 'live:send:msg:record:' . $liveId;
        $num = $redis->lLen($cacheKey);

        if ($num >= $len)
        {
            $redis->rPop($cacheKey);
        }

        $record = json_encode($msgData);

        $redis->lPush($cacheKey, $record);

        $redis->expire($cacheKey, 3600); // 缓存1小时过期时间
    }

    /**
     * 优质连线直播间 记录最近消息
     * @param array $msgData
     * @param int $liveRecordId
     * @return void
     */
    private static function ZyhsGoodRtcLiveSendmsgCacheAdd(array $msgData, int $liveRecordId)
    {
        $len = 20;
        // 获取redis操作对象
        $redis = CacheManageZyhs::getInstance()->redisHandler;

        $cacheKey = 'good_rtc_live:send:msg:record:' . $liveRecordId;
        $num = $redis->lLen($cacheKey);

        if ($num >= $len)
        {
            $redis->rPop($cacheKey);
        }

        $record = json_encode($msgData);

        $redis->lPush($cacheKey, $record);

        $redis->expire($cacheKey, 86400); // 缓存1小时过期时间
    }

    /**
     * 发送消息给Client端
     * @param string $msg 返回消息
     * @param array $who 发送给谁
     * @param string $action 发送的消息类型
     * @param mixed $data 发送的数据
     * @param array $exclude_client_ids 发送组消息的时候，排除哪些clientids
     */
    private static function sendMsgNow(string $msg = '', array $who = [], string $action = '', $data = [], array $exclude_client_ids = []): void
    {

        try
        {
            //返回数据
            $return = [
                'is_success' => true,
                'msg' => '',
            ];

            //设置注册地址
            Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');

            //发送给谁
            switch ($who['type'])
            {
                case self::WHO_TYPE_UID:
                {
                    if ($who['live_id'] > 0)
                    {
                        $isOnline = self::getUidIsOnline($who['live_id'], $who['id']);
                    } else
                    {
                        $isOnline = Gateway::isUidOnline($who['id']);
                    }
                    if ($isOnline)
                    {//在线
                        Gateway::sendToUid($who['id'], self::msg($msg, $action, $data));
                    } else
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '该用户（' . $who['id'] . '）不在线，发送失败！';
                    }
                    break;
                }
                case self::WHO_TYPE_CLIENT_ID:
                {
                    if (Gateway::isOnline($who['id']))
                    {//在线
                        Gateway::sendToClient($who['id'], self::msg($msg, $action, $data));
                    } else
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '该用户（' . $who['id'] . '）不在线，发送失败！';
                    }
                    break;
                }
                case self::WHO_TYPE_LIVER:
                {
                    Gateway::sendToGroup($who['id'] . '_liver_or_admin', self::msg($msg, $action, $data), $exclude_client_ids);
                    break;
                }
                case self::WHO_TYPE_ALL:
                {
//                    //发送App用户
                    Gateway::sendToGroup($who['id'] . '_app_user_group', self::msg($msg, $action, $data), $exclude_client_ids);
//                    //发送主播端、后台、主播管理员
                    Gateway::sendToGroup($who['id'] . '_liver_or_admin', self::msg($msg, $action, $data), $exclude_client_ids);
                    break;
                }
                case self::WHO_TYPE_APP_USER:
                {
                    Gateway::sendToGroup($who['id'] . '_app_user_group', self::msg($msg, $action, $data), $exclude_client_ids);
                    break;
                }
                case self::WHO_TYPE_ASTRO:
                    $astrId = self::WHO_TYPE_ASTRO . ':' . $who['id'];
                    if (Gateway::isUidOnline($astrId))
                    {//在线
                        Gateway::sendToUid($astrId, self::msg($msg, $action, $data));
                    } else
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '该占星师（' . $who['id'] . '）不在线，发送失败！';
                    }
                    break;
                case self::WHO_TYPE_ZYHS_UID:
                    $zyhsConsultUid = self::WHO_TYPE_ZYHS_UID . ':' . $who['id'];
                    if (Gateway::isUidOnline($zyhsConsultUid))
                    {//在线
                        Gateway::sendToUid($zyhsConsultUid, self::msg($msg, $action, $data));
                    } else
                    {
                        $return['is_success'] = false;
                        $return['msg'] = '该准有好事 咨询用户（' . $who['id'] . '）不在线，发送失败！';
                    }
                    break;
                case self::WHO_TYPE_GOOD_RTC_LIVE_ROOM:
                    //  发送 优质连线 直播间 信息
                    Gateway::sendToGroup('zyhs_good_rtc_live_room_' . $who['id'], self::msg($msg, $action, $data), $exclude_client_ids);
                    break;
            }

            //写日志
//            Util::logWriteOfLiveChatMsg([
//                'who_type' => $who['type'],//发送类型
//                'who_id' => $who['id'],//直播间id 或者 用户id 或者 clientid
//                'who_uid' => $who['uid'],//发送消息的用户id
//                'msg_action' => $action,//发送的消息类型
//                'msg_content' => json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),//发送的消息内容（不能为空，string）]);
//            ]);

        }
        catch (\Exception $e)
        {
            Log::write("websocket sendMsgNow 发送【" . $msg . "】消息校验错误：" . $return['msg'] . '【who=' . json_encode($who, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',action=' . json_encode($action, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',data=' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',exclude_client_ids=' . json_encode($exclude_client_ids, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】' . '【' . json_encode($e, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】', 'error', true);
        }
    }

    /**
     * 把消息加入队列
     * @param string $msg 返回消息
     * @param array $who 发送给谁
     * @param string $action 发送的消息类型
     * @param mixed $data 发送的数据
     * @param array $exclude_client_ids 发送组消息的时候，排除哪些clientids
     * @return bool 是否现在发送；true=》现在发送；false=》现在不发送，加入队列发送 ；
     */
    private static function sendLimit(string $msg = '', array $who = [], string $action = '', $data = [], array $exclude_client_ids = []): bool
    {
        try
        {
            $is_limit = false;//是否进行限制
            $is_send_now = true;//是否现在发送
            $max_count_per_min = -1;//每分钟最多发送的数量
//        $delay = 0;//延迟时间
            switch ($action)
            {
                case self::MSG_TYPE_POP_ZAN_SIGN:
                {
                    $is_limit = true;
                    $max_count_per_min = (int)config('websocket.max_count_per_min_of_zan');
                    break;
                }
                case self::MSG_TYPE_POP_USER_COME:
                {
                    $is_limit = true;
                    $max_count_per_min = (int)config('websocket.max_count_per_min_of_user_come');
                    break;
                }
            }

            if ($is_limit && $max_count_per_min !== -1)
            {
                $cache_key = 'live_max_count_per_min:' . $action . ':' . date("Y-m-d_H:i");//每分钟发的最大数量
                $cache_count = Cache::get($cache_key, -1);//当前的数量

                if (-1 === $cache_count)
                {//没有设置值
                    Cache::set($cache_key, 1, 70);
                    $is_send_now = true;
                } else if ($cache_count < $max_count_per_min)
                {
                    Cache::inc($cache_key);
                    $is_send_now = true;
                } else if ($cache_count > $max_count_per_min)
                {
                    //丢弃
                    $is_send_now = false;
                }
            }
        }
        catch (\Exception $e)
        {
            Log::write("websocket sendLimit 发送【" . $msg . "】消息校验错误！" . '【who=' . json_encode($who, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',action=' . json_encode($action, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',data=' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',exclude_client_ids=' . json_encode($exclude_client_ids, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】' . '【' . json_encode($e, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】', 'error', true);
        }

        return $is_send_now;
    }

    /**
     * 发送给*，参数组装
     * @param string $type 发送类型：发送给谁的类型【
     * WHO_TYPE_ALL（all）=》发送给所有人（群组名字为：$live_id）；
     * WHO_TYPE_UID（uid）：发送给某个人；
     * WHO_TYPE_LIVER（liver）：发送给主播端 和 后台管理的ws用户 和 用户主播管理员（群组名字为：$live_id_liver_or_admin）
     * WHO_TYPE_CLIENT_ID（client_id）：发送给某个人；
     * WHO_TYPE_APP_USER（app_user_group）：只发送给准了app的用户（群组名字为：$live_id_app_user_group）；
     * 】
     * @param int $liveId 向指定的直播间内发消息 0不限制
     * @param string $liveid_or_uid_or_clientid 组的id
     * @param string $your_uid 用户id （当用户发送弹幕的时候，用来排除这个用户）
     * @return array|string[]
     */
    public static function who(string $type = '', int $liveId, string $liveid_or_uid_or_clientid = '', string $your_uid = ''): array
    {
        return [
            'type' => $type,
            'live_id' => $liveId,
            'id' => $liveid_or_uid_or_clientid,
            'uid' => $your_uid,
        ];
    }


    /**
     * websocket返回数据
     * @param int $code 200：成功；0：失败；
     * @param string $msg 返回消息内容
     * @param string $action websocket的请求action
     * @param array $data 返回数据
     * @return false|string
     */
    public static function show(int $code = 0, string $msg = '', string $action = '', array $data = [])
    {
        $data = [
            'code' => $code,
            'msg' => $msg,
            'action' => $action,
            'data' => empty($data) ? null : $data,
        ];

        return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }


    /**
     * websocket返回消息给用户
     * @param string $msg 消息内容
     * @param string $type 类型（online_info、pop_header、pop_gift...等）
     * @param mixed $data 数据
     * @param int $code
     * @return false|string
     */
    public static function msg(string $msg = '', string $type = '', $data = [], int $code = 200)
    {
        $data = [
            'code' => $code,
            'msg' => $msg,
            'data' => ['type' => $type, $type => $data],
        ];

        return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }


    /**
     * header校验
     * @param array $headers
     * @param string $live_id
     * @param int $is_liver 是否是主播端，0：不是；1：是；
     * @param string $client_id
     * @return array
     * @throws ApiException|\Exception
     */
    public static function auth(array $headers = [], string $live_id = '', int $is_liver = 0, string $client_id = '', int $is_liver_admin = 0): array
    {
        //返回数据
        $data = [
            'code' => 0,
            'msg' => '',
            'action' => 'login',
            'app_type' => $headers['app-type'],
            'uid' => 0,
            'uen' => '',
            'name' => '',
            'avatar' => '',
            'vip_level' => -1,
            'rank_index' => -1,
            'user' => [],
            'sign' => 0,
            'user_type' => self::USER_TYPE_APP_USER,//用户类型：【0：用户App端；1：是主播App端；2：是准了后台管理员；3：用户端管理员；】
            'version' => '',//用户的版本号（在主播邀请连麦的时候，用于判断是否支持连麦），只有用户App端存在；
        ];


        // 基础参数校验
        if (isset($headers['app-type']))
        {
            //app端，用户端和主播端和用户管理员端 登录，权限认证
            if ($headers['app-type'] !== 'h5' && strpos(config('websocket.app_types'), $headers['app-type']) !== false)
            {
                $msg_pre = $is_liver ? '主播端：' : '用户端：';

                // APP 检查是否存在sign参数
                if (!isset($headers['sign']))
                {
                    Log::write(self::LOG_PRE . '请求缺少sign', 'error', true);
                    $data['msg'] = $msg_pre . '请求缺少sign';
                    return $data;
                }

                // sign验签
                if (!IAuth::checkSignPass($headers, $is_liver ? 'live_app' : 'app'))
                {
                    Log::write(self::LOG_PRE . 'sign验签失败', 'error', true);
                    $data['msg'] = $msg_pre . 'sign验签失败';
                    return $data;
                }

                //验签成功，通过 是否存在 access-token 判断  主播端或者用户端 是否登录
                if (!isset($headers['access-token']) || $headers['access-token'] === '')
                {//没有登录
                    if ($is_liver)
                    {//主播端，不能不登陆，返回错误信息
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '前往登录';
                    } else
                    {//用户端，可以不登陆，进入直播间
                        $data['code'] = 200;
                        $data['msg'] = $msg_pre . 'ws登录成功，但是用户未登录！';
                    }

                    return $data;
                }

                //已登录，access-token AES解密
                $accessUserToken = (new Aes())->decryptWithOpenssl($headers['access-token'], $is_liver ? 'live_app' : 'app');
                if (empty($accessUserToken) || !preg_match('/\|\|/', $accessUserToken))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '登录过期';
                    return $data;
                }

                [$token, $id] = explode('||', $accessUserToken);
                $id = (int)($id);
                $data['code'] = 200;
                $data['msg'] = '登录成功！';

                if ($is_liver)
                {//是主播端
                    $userinfo = (new LiveAnchorInfo())->getInfoById($id);
                } else
                {//用户端
                    $data['uid'] = $id;
                    $data['version'] = $headers['version'];
                    $map['id'] = $id;
                    $map['token'] = $token;

                    //这里抛出了异常，需要捕捉以下，否则，走MyEvent的onMessage里的catch，不发给用户错误信息
                    try
                    {
                        $userinfo = (new User())->getUserinfo($map);
                        if ($userinfo['rid'])
                        {
                            $recordInfo = (new Recode())->getInfoById($userinfo['rid']);
                            $data['sign'] = (int)(explode('-', $recordInfo['sun'])[0]);
                        }

                        if ((int)$data['sign'] === 0)
                        {
                            Log::write('-------------------星座信息没有；用户信息：' . json_encode($userinfo) . ',档案信息：' . json_encode($recordInfo ?? ''), 'log', true);
                        }
                    }
                    catch (\Exception $e)
                    {
                        $userinfo = null;
                        Log::write('websocket Auth 处理异常：【message=' . $e->getMessage() . 'code=' . $e->getCode() . 'file=' . $e->getFile() . 'line=' . $e->getLine() . '】' . '【headers=' . json_encode($headers) . ',live_id=' . json_encode($live_id) . ',client_id=' . json_encode($client_id) . '】', 'error', true);
                    }

                    if ($is_liver_admin)
                    {//用户主播管理员
                        $sucess = true;//todo 校验用户是否是主播管理员
                        if ($sucess)
                        {//校验成功
                            $data['user_type'] = self::USER_TYPE_APP_ADMIN;
                        } else
                        {//校验失败
                            $data['code'] = 405;
                            $data['msg'] = $msg_pre . '您不是用户管理员！';
                            Log::write('websocket Auth 处理异常：您不是用户管理员【用户信息：' . json_encode($userinfo) . ',档案信息：' . json_encode($recordInfo ?? '') . '】', 'log', true);
                            return $data;
                        }
                    }

                }

                if (!$userinfo || empty($userinfo))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '当前账号登录过期，请您重新登录。';
                    return $data;
                }

                if ($is_liver)
                {//主播端
                    if (!$userinfo['status'])
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                        return $data;
                    }
                } else
                {//用户端、用户主播管理员端
                    if ((int)$userinfo['forbidden'] === 1)
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                        return $data;
                    }
                }

                // 判定时间是否过期
                if (isset($map['token']) && time() > $userinfo['token_expire'])
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                    return $data;
                }

                if ($is_liver)
                {//主播端
                    if ((int)$live_id !== (int)$userinfo['live_id'])
                    {
                        Log::write(self::LOG_PRE . '主播端登录成功，但是ws传过来的live_id和解析出来的不一致：ws live_id=' . $live_id . 'userinfo=' . json_encode($userinfo, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'error', true);
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                        return $data;
                    } else
                    {
                        Log::write(self::LOG_PRE . '主播端登录成功:userinfo=' . json_encode($userinfo, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
                        $data['user_type'] = self::USER_TYPE_APP_LIVER;

                        $data['user'] = $userinfo;
                        $data['name'] = $userinfo['name'] ?? '';
                        $data['avatar'] = $userinfo['avatar'] ?? '';
                        $data['uid'] = $userinfo['id'] ?? '';
                    }
                } else
                {//用户端、用户主播管理员端
                    $data['user'] = $userinfo;
                    $data['uen'] = $userinfo['uen'];
                    $data['name'] = $userinfo['nickname'];
                    $data['avatar'] = $userinfo['avatar'];
                    try
                    {
                        $data['vip_level'] = (new ZlUserVip())->getUserVipLevel($id);
                        $data['rank_index'] = (new LiveUserContribution())->getUserRanking($live_id, $userinfo['id'], 50);
                    }
                    catch (\Exception $e)
                    {
                        Log::write("用户端登录获取VIP等级及排行信息：{$e->getMessage()};File:{$e->getFile()};Line:{$e->getLine()}", 'error', true);
                    }
                }

                return $data;
            }

            //h5端（用户分享出去的） 登录，权限认证
            if ($headers['app-type'] === 'h5')
            {   // h5 前后端加密
                //签名规则：md5(md5(time=1629095750&action=login&live_id=1&app-type=h5)+'BvHa9WjkN1GceKg5')
                $sginData = [
                    'time' => $headers['time'],
                    'action' => 'login',
                    'live_id' => $live_id,
                    'app-type' => $headers['app-type'],
                ];
                $signRes = md5(md5(http_build_query($sginData)) . config('websocket.h5_key_of_ws_login'));

                if ($signRes === $headers['sign'])
                {
                    $data['code'] = 200;
                    $data['msg'] = 'h5登录成功！';
                    return $data;
                }

                $data['msg'] = 'h5签名失败！';
                return $data;
            }


            //admin端（准了后台管理页面）登录，权限认证
            if ($headers['app-type'] === 'admin')
            {   // h5 前后端加密
                //签名规则：md5(md5(time=1629095750&action=login&live_id=1&app-type=h5)+'BvHa9WjkN1GceKg5')
                $sginData = [
                    'time' => $headers['time'],
                    'action' => 'login',
                    'live_id' => $live_id,
                    'app-type' => $headers['app-type'],
                    'admin_id' => $headers['admin_id'],
                ];
                $signRes = md5(md5(http_build_query($sginData)) . config('websocket.admin_key_of_ws_login'));

                if ($signRes === $headers['sign'])
                {
                    $cache_admin_id = (int)Cache::get('live_admin_ws:' . $headers['admin_id']);
                    if ($cache_admin_id === (int)$headers['admin_id'])
                    {
                        $data['code'] = 200;
                        $data['msg'] = 'admin登录成功！';
                        $data['user_type'] = self::USER_TYPE_ADMIN;
                        $data['uid'] = $cache_admin_id;
                    } else
                    {
                        $data['msg'] = 'admin登录失败，请重新登录准了后台！';
                        Log::write(self::LOG_PRE . $data['msg'] . ',params=' . json_encode($headers), 'error', true);
                    }

                    return $data;
                }

                $data['msg'] = 'admin签名失败！';
                return $data;
            }

            $data['msg'] = '当前设备不支持';
            return $data;
        }

        $data['msg'] = '当前设备不支持';
        return $data;
    }

    /**
     * header校验
     * @param array $headers
     * @param string $client_id
     * @return array
     * @throws ApiException|\Exception
     */
    public static function zhunleAuth(array $headers = [], string $client_id = ''): array
    {
        //返回数据
        $data = [
            'code' => 0,
            'msg' => '',
            'action' => 'login',
            'app_type' => $headers['app-type'],
            'uid' => 0,
            'uen' => '',
            'name' => '',
            'avatar' => '',
            'vip_level' => -1,
            'rank_index' => -1,
            'user' => [],
            'user_type' => self::USER_TYPE_APP_USER,//用户类型：【0：用户App端；1：是主播App端；2：是准了后台管理员；3：用户端管理员；】
            'version' => '',//用户的版本号（在主播邀请连麦的时候，用于判断是否支持连麦），只有用户App端存在；
        ];

        $loginType = $headers['login_type'];
        // 基础参数校验
        if (isset($headers['app-type']))
        {
            //app端，用户端和主播端和用户管理员端 登录，权限认证
            if (strpos(config('websocket.app_types'), $headers['app-type']) !== false)
            {
                $msg_pre = '准了APP用户端：';
                // APP 检查是否存在sign参数
                if (!isset($headers['sign']))
                {
                    Log::write(self::LOG_PRE . '请求缺少sign', 'error', true);
                    $data['msg'] = $msg_pre . '请求缺少sign';
                    return $data;
                }
                // sign验签
                if (!IAuth::checkSignPass($headers))
                {
                    Log::write(self::LOG_PRE . 'sign验签失败', 'error', true);
                    $data['msg'] = $msg_pre . 'sign验签失败';
                    return $data;
                }
                //验签成功，通过 是否存在 access-token 判断  主播端或者用户端 是否登录
                if (!isset($headers['access-token']) || $headers['access-token'] === '')
                {//没有登录
                    if ($loginType === 1)
                    {   //  优质连线直播间 允许未登录
                        $data['code'] = 200;
                        $data['msg'] = $msg_pre . 'ws登录成功，但是用户未登录！';
                    } else
                    {   //  必须登录
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '前往登录';
                    }
                    return $data;
                }
                //已登录，access-token AES解密
                $accessUserToken = (new Aes())->decryptWithOpenssl($headers['access-token']);
                if (empty($accessUserToken) || !preg_match('/\|\|/', $accessUserToken))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '登录过期';
                    return $data;
                }
                [$token, $id] = explode('||', $accessUserToken);
                $id = (int)($id);
                $data['code'] = 200;
                $data['msg'] = '登录成功！';

                $data['uid'] = $id;
                $data['version'] = $headers['version'];
                $map['id'] = $id;
                $map['token'] = $token;
                //这里抛出了异常，需要捕捉以下，否则，走MyEvent的onMessage里的catch，不发给用户错误信息
                try
                {
                    $userinfo = (new User())->getUserinfo($map);
                }
                catch (\Exception $e)
                {
                    $userinfo = null;
                    Log::write('websocket Auth 处理异常：【message=' . $e->getMessage() . 'code=' . $e->getCode() . 'file=' . $e->getFile() . 'line=' . $e->getLine() . '】' . '【headers=' . json_encode($headers) . ',client_id=' . json_encode($client_id) . '】', 'error', true);
                }
                if (!$userinfo || empty($userinfo))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '当前账号登录过期，请您重新登录。';
                    return $data;
                }
                //用户端、用户主播管理员端
                if ((int)$userinfo['forbidden'] === 1)
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                    return $data;
                }
                // 判定时间是否过期
                if (isset($map['token']) && time() > $userinfo['token_expire'])
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                    return $data;
                }
                //用户端、用户主播管理员端
                $data['user'] = $userinfo;
                $data['uen'] = $userinfo['uen'];
                $data['name'] = $userinfo['nickname'];
                $data['avatar'] = $userinfo['avatar'];

                return $data;
            }
            $data['msg'] = '当前设备不支持';
            return $data;
        }
        $data['msg'] = '当前设备不支持';
        return $data;
    }

    /**
     * 咨询 登录 权限校验   ------ 废弃
     * @param array $headers
     * @param int $isConsult
     * @param string $client_id
     * @return array
     * @throws \Exception
     */
    public static function consultAuth(array $headers = [], int $isConsult = 0, string $client_id = '')
    {
        //返回数据
        $data = [
            'code' => 0,
            'msg' => '',
            'app_type' => $headers['app-type'],
            'action' => 'login',
            'uid' => 0,
            'uen' => '',
            'name' => '',
            'avatar' => '',
            'user' => [],
            'user_type' => self::USER_TYPE_APP_USER,//用户类型：【0：用户App端；1：是主播App端；2：是准了后台管理员；3：用户端管理员；】
            'version' => $headers['version'],//用户的版本号（在主播邀请连麦的时候，用于判断是否支持连麦），只有用户App端存在；
        ];

        // 基础参数校验
        if (isset($headers['app-type']))
        {
            //app端，用户端和主播端和用户管理员端 登录，权限认证
            if ($headers['app-type'] !== 'h5' && strpos(config('websocket.app_types'), $headers['app-type']) !== false)
            {
                $msg_pre = $isConsult ? '占星师端：' : '用户端：';

                // APP 检查是否存在sign参数
                if (!isset($headers['sign']))
                {
                    Log::write(self::LOG_PRE . '请求缺少sign', 'error', true);
                    $data['msg'] = $msg_pre . '请求缺少sign';
                    return $data;
                }

                // sign验签
                if (!IAuth::checkSignPass($headers, $isConsult ? 'astradmin' : 'app'))
                {
                    Log::write(self::LOG_PRE . 'sign验签失败', 'error', true);
                    $data['msg'] = $msg_pre . 'sign验签失败';
                    return $data;
                }

                //验签成功，通过 是否存在 access-token 判断  主播端或者用户端 是否登录
                if (!isset($headers['access-token']) || $headers['access-token'] === '')
                {//没有登录
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '前往登录';
                    return $data;
                }

                //已登录，access-token AES解密
                $accessUserToken = (new Aes())->decryptWithOpenssl($headers['access-token'], $isConsult ? 'astradmin' : 'app');
                if (empty($accessUserToken) || !preg_match('/\|\|/', $accessUserToken))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '登录过期';
                    return $data;
                }

                [$token, $id] = explode('||', $accessUserToken);
                $id = (int)($id);
                $data['code'] = 200;
                $data['msg'] = '登录成功！';

                if ($isConsult)
                {//是主播端
                    $consultAccountInfo = (new Zlcr3ConsultAccount())->getInfoById($id);
                    $isConsult = $consultAccountInfo && $consultAccountInfo['consult_status'] === 1 ? 1 : 0;
                    if (!$isConsult)
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '咨询师不存在';
                        return $data;
                    }
                    $zlcrAstrModel = new ZlcrAstr();
                    $userinfo = $zlcrAstrModel->getInfoById($id);
                } else
                {//用户端
                    $data['uid'] = $id;
                    $map['id'] = $id;
                    $map['token'] = $token;

                    //这里抛出了异常，需要捕捉以下，否则，走MyEvent的onMessage里的catch，不发给用户错误信息
                    try
                    {
                        $userinfo = (new User())->getUserinfo($map);
                    }
                    catch (\Exception $e)
                    {
                        $userinfo = null;
                    }
                }

                if (!$userinfo || empty($userinfo))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '当前账号登录过期，请您重新登录。';
                    return $data;
                }

                if ($isConsult)
                {//主播端
                    if (!$userinfo['status'])
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                        return $data;
                    }
                } else
                {//用户端、用户主播管理员端
                    if ((int)$userinfo['forbidden'] === 1)
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                        return $data;
                    }
                }

                // 判定时间是否过期
                if (time() > $userinfo['token_expire'])
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                    return $data;
                }

                if ($isConsult)
                {//主播端
                    Log::write(self::LOG_PRE . '咨询师端登录成功:userinfo=' . json_encode($userinfo, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
                    $data['user_type'] = self::USER_TYPE_APP_ASTRO;

                    $data['user'] = $userinfo;
                    $data['name'] = $userinfo['name'] ?? '';
                    $data['avatar'] = $userinfo['avatar'] ?? '';
                    $data['uid'] = $userinfo['id'] ?? '';
                } else
                {//用户端、用户主播管理员端
                    $data['user'] = $userinfo;
                    $data['uen'] = $userinfo['uen'];
                    $data['name'] = $userinfo['nickname'];
                    $data['avatar'] = $userinfo['avatar'];
                }

                return $data;
            }

            $data['msg'] = '当前设备不支持';
            return $data;
        }

        $data['msg'] = '当前设备不支持';
        return $data;
    }

    /**
     * 咨询 登录 权限校验   ------ 准有好事
     * @param array $headers
     * @param int $isConsult
     * @param string $client_id
     * @return array
     * @throws \Exception
     */
    public static function zyhsConsultAuth(array $headers = [], int $isConsult = 0, string $client_id = '')
    {
        //返回数据
        $data = [
            'code' => 0,
            'msg' => '',
            'app_type' => $headers['app-type'],
            'action' => 'login',
            'uid' => 0,
            'uen' => '',
            'name' => '',
            'avatar' => '',
            'user' => [],
            'user_type' => self::USER_TYPE_APP_ZYHS_USER,//用户类型：【0：用户App端；1：是主播App端；2：是准了后台管理员；3：用户端管理员；】
            'version' => $headers['version'],//用户的版本号（在主播邀请连麦的时候，用于判断是否支持连麦），只有用户App端存在；
            'live_record_id' => 0,  // 直播记录ID  live_type字段大于0
            'live_type' => 0,   // 登录类型 0=>连线咨询  1=>优质连线直播间
        ];

        // 基础参数校验
        if (isset($headers['app-type']))
        {
            //app端，用户端和主播端和用户管理员端 登录，权限认证
            if (strpos(config('websocket.app_types'), $headers['app-type']) !== false)
            {
                $msg_pre = $isConsult ? '占星师端：' : '准有好事用户端：';

                // APP 检查是否存在sign参数
                if (!isset($headers['sign']))
                {
                    Log::write(self::LOG_PRE . '请求缺少sign', 'error', true);
                    $data['msg'] = $msg_pre . '请求缺少sign';
                    return $data;
                }

                // sign验签
                if (isset($headers['app-type']) && in_array($headers['app-type'], ['ios', 'android'], true))
                {
                    if (!IAuth::checkSignPass($headers, $isConsult ? 'astradmin' : 'zyhsapp'))
                    {
                        Log::write(self::LOG_PRE . 'sign验签失败', 'error', true);
                        $data['msg'] = $msg_pre . 'sign验签失败';
                        return $data;
                    }
                } else if (isset($headers['app-type']) && in_array($headers['app-type'], ['flutter-web', 'flutter-mac', 'flutter-win', 'flutter-linux'], true))
                {
                    if (md5($headers['app-type'] . $headers['time'] . $headers['version'] . 'b0Rrpe5cuSONdDEz') !== $headers['sign'])
                    {
                        Log::write('flutter ws 验签错误：app-type=' . $headers['app-type'] . ',time=' . $headers['time'] . ',version=' . $headers['version'] . ',sign=' . $headers['sign'], 'error', true);
                        $data['msg'] = $msg_pre . 'flutter ws sign验签失败';
                        return $data;
                    }
                }

                if (isset($headers['live_type'], $headers['live_record_id']))
                {
                    $data['live_record_id'] = (int)$headers['live_record_id'];
                    $data['live_type'] = (int)$headers['live_type'];
                }

                if (!in_array($data['live_type'], [0, 1], true) || ($data['live_type'] === 1 && $data['live_record_id'] <= 0))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '登录失败';
                    return $data;
                }

                //验签成功，通过 是否存在 access-token 判断  主播端或者用户端 是否登录
                if (!isset($headers['access-token']) || $headers['access-token'] === '')
                {//没有登录
                    if (!$isConsult && $data['live_type'] === 1)
                    {   //用户端，可以不登陆，进入直播间
                        $data['code'] = 200;
                        $data['msg'] = $msg_pre . 'ws登录成功，但是用户未登录！';
                    } else
                    {   // 占星师端 必须登录
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '前往登录';
                    }
                    return $data;
                }

                //已登录，access-token AES解密
                $accessUserToken = (new Aes())->decryptWithOpenssl($headers['access-token'], $isConsult ? 'astradmin' : 'zyhsapp');
                if (empty($accessUserToken) || !preg_match('/\|\|/', $accessUserToken))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '登录过期';
                    return $data;
                }

                [$token, $id] = explode('||', $accessUserToken);
                $id = (int)($id);
                $data['code'] = 200;
                $data['msg'] = '登录成功！';

                if ($isConsult)
                {//是主播端
                    $consultAccountInfo = (new Zlcr3ConsultAccount())->getInfoById($id);
                    $isConsult = $consultAccountInfo && $consultAccountInfo['consult_status'] === 1 ? 1 : 0;
                    if (!$isConsult)
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '咨询师不存在';
                        return $data;
                    }
                    $zlcrAstrModel = new ZlcrAstr();
                    $userinfo = $zlcrAstrModel->getInfoById($id);
                } else
                {//用户端
                    $data['uid'] = $id;

                    //这里抛出了异常，需要捕捉以下，否则，走MyEvent的onMessage里的catch，不发给用户错误信息
                    try
                    {
                        $userinfo = (new ZyhsUser())->getInfoById($id);
                        if ($userinfo['token'] !== $token)
                        {
                            $userinfo = null;
                        }
                    }
                    catch (\Exception $e)
                    {
                        $userinfo = null;
                    }
                }

                if (!$userinfo || empty($userinfo))
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '当前账号登录过期，请您重新登录。';
                    return $data;
                }

                if ($isConsult)
                {//主播端
                    if (!$userinfo['status'])
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                        return $data;
                    }
                } else
                {//用户端、用户主播管理员端
                    if ((int)$userinfo['forbidden'] === 1)
                    {
                        $data['code'] = 405;
                        $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                        return $data;
                    }
                }

                // 判定时间是否过期
                if (time() > $userinfo['token_expire'])
                {
                    $data['code'] = 405;
                    $data['msg'] = $msg_pre . '您的账户登录过期，请重新登录。';
                    return $data;
                }

                if ($isConsult)
                {//主播端
                    Log::write(self::LOG_PRE . '咨询师端登录成功:userinfo=' . json_encode($userinfo, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
                    $data['user_type'] = self::USER_TYPE_APP_ASTRO;

                    $data['user'] = $userinfo;
                    $data['name'] = $userinfo['nickname'] ?? '';
                    $data['avatar'] = $userinfo['avatar'] ?? '';
                    $data['uid'] = (int)($userinfo['id'] ?? 0);
                } else
                {//用户端、用户主播管理员端
                    $data['user_type'] = self::USER_TYPE_APP_ZYHS_USER;
                    $data['user'] = $userinfo;
                    $data['uen'] = $userinfo['uen'];
                    $data['name'] = $userinfo['nickname'];
                    $data['avatar'] = $userinfo['avatar'];
                }

                return $data;
            }

            $data['msg'] = '当前设备不支持';
            return $data;
        }

        $data['msg'] = '当前设备不支持';
        return $data;
    }

    /**
     * 判断用户 是否在线
     * @param int $type 1=>用户  2=>占星师
     * @param int $id 用户uid ｜ 咨询师ID
     * @return bool
     */
    public static function getUserIsOnline(int $type, int $id)
    {
        try
        {
            Gateway::$registerAddress = config('websocket.register_ip') . ':' . config('websocket.register_port');
            if ($type === 2)
            {
                $clientUid = self::WHO_TYPE_ASTRO . ':' . $id;
            } else
            {
                $clientUid = $id;
            }
            $res = Gateway::isUidOnline($clientUid);
            return $res ? true : false;
        }
        catch (\Exception $e)
        {
            \think\Log::write(__CLASS__ . ' => ' . __FUNCTION__ . '()异常：' . $e->getMessage() . '失败信息：' . $e->getFile() . ':' . $e->getLine(), 'error', true);
            return false;
        }
    }
}
